package fr.lelouet.monitoring.impl

import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fr.lelouet.monitoring.UsageMonitor 
import fr.lelouet.monitoring.datahandlers.SnapshotDataHandler;
import fr.lelouet.monitoring.snapshot.HVSnapshot;
import fr.lelouet.monitoring.snapshot.SnapshotReceiver;
import fr.lelouet.monitoring.snapshot.VMSnapshot;
import fr.lelouet.monitoring.snapshot.receivers.DirectoryWriter;
import fr.lelouet.monitoring.snapshot.receivers.LoggerReceiver;
import fr.lelouet.monitoring.stress.CPUStress;
import fr.lelouet.monitoring.stress.DiskStress;
import fr.lelouet.tools.sequentialIterators.ClockDependant 


/** stress several resources and monitor the vms and energy in a directory<br />
 * switching from a Stress configuration to another is done after receiving a {@link HVSnapshot}*/
class StressAndMonitor implements SnapshotReceiver{
	
	private static final Logger logger  = LoggerFactory.getLogger(StressAndMonitor.class)
	
	protected ClockDependant<UsageMonitor> ticked= new ClockDependant<UsageMonitor>()
	
	protected CPUStress cpuStress = new CPUStress()
	protected List<Long> cpuLoads
	protected int cpuLoadsIndex=0
	
	protected DiskStress diskStress = new DiskStress()
	protected List<String> diskLoads
	protected int diskLoadsIndex=0
	
	
	protected boolean snapshotReceived=true
	
	@Override
	public void configure(Properties prop) {
	}
	
	@Override
	public void vmModification(VMSnapshot snapshot, HVSnapshot owner) {
	}
	
	@Override
	public void hypervisorModification(HVSnapshot toShow) {
		snapshotReceived =true
	}
	
	/** true when all the possible stress configuration have been run*/
	protected boolean finished = false
	
	/** the properties used for the kvmHypervisor*/
	Properties properties = new Properties()
	
	void run() {
		logger.debug("calibrating")
		calibrateLoads()
		logger.debug ("generating the monitor")
		KvmHypervisor hypervisor = new KvmHypervisor(properties)
		SnapshotDataHandler snaps = new SnapshotDataHandler()
		hypervisor.setHandlers([snaps])
		hypervisor.setRequestScheduler (ticked)
		snaps.setReceivers([
			new DirectoryWriter(),
			new LoggerReceiver(),
			this
		])
		logger.debug ("starting the stress tools")
		startNewStress()
		logger.debug ("starting the monitor")
		Thread.start { hypervisor.run() }
		finished = false
		while(!finished) {
			tick()
		}
		logger.debug "monitoring ended"
		hypervisor.stop()
		stopStresses()
	}
	
	void calibrateLoads() {
		cpuLoads = cpuStress.calibrateLoopSize(200)
		logger.info "CPU stress parameters after calibration are : {}", cpuLoads
		diskLoads = diskStress.calibrateBlocSize(200)
		logger.info "DISK stress parameters after calibration : {}", diskLoads
	}
	
	void startNewStress() {
		resetConfiguration()
		configurationChanged()
		Thread.start {cpuStress.run()}
		Thread.start {diskStress.run()}
	}
	
	void resetConfiguration() {
		cpuLoadsIndex=0
		diskLoadsIndex=0
	}
	
	/** time between the modification of the parameters and the querying of the sensors, in ms */
	long configurationDelay = 1000
	
	/** a tick results in the next computation phase: the {@link UsageMonitor}s get the values, then the configuration is changed*/
	void tick() {
		logger.debug "ticking"
		snapshotReceived=false
		Thread.sleep(configurationDelay)
		ticked.tick()
		logger.debug ("waiting for the snapshot")
		while(!snapshotReceived) {
			Thread.sleep(1)
		}
		logger.debug("making next configuration")
		nextConfiguration()
	}
	
	void nextConfiguration() {
		cpuLoadsIndex++
		if(cpuLoadsIndex>=cpuLoads.size()) {
			cpuLoadsIndex=0
			cpuLoopDone()
		}
		configurationChanged()
	}
	
	void cpuLoopDone() {
		diskLoadsIndex++
		if(diskLoadsIndex>=diskLoads.size()) {
			diskLoadsIndex=0
			diskLoopDone()
		}
	}
	
	void diskLoopDone() {
		finished=true
	}
	
	void configurationChanged() {
		cpuStress.setLoopSize cpuLoads.get(cpuLoadsIndex)
		diskStress.setBlocSize(diskLoads.get(diskLoadsIndex))
		logger.info "configuration set to : CPULoopSize={}, DISKBlocSize={}", cpuLoads.get(cpuLoadsIndex), diskLoads.get(diskLoadsIndex)
	}
	
	void stopStresses() {
		cpuStress.stop()
		diskStress.stop()
		ticked.tick()
	}
	
	static void main(args) {
		StressAndMonitor stresser = new StressAndMonitor()
		stresser.setConfigurationDelay 1000*2
		Properties props = new Properties()
		props.setProperty(KvmHypervisor.POWERURI_KEY, "plugwise://000D6F000076A98E")
		props.setProperty(ProcessMonitor.MONITOREDEVENTS_KEY, ProcessMonitor.ALLEVENTS_TOKEN)
		stresser.setProperties props
		stresser.run()
		System.out.println("finished")
	}
}
